package com.wyl.auth.common.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.wyl.auth.common.service.RedisService;
import com.wyl.auth.dto.RedisDto;
import com.wyl.auth.req.KeyReq;
import com.wyl.auth.vo.RedisIndexVo;
import com.wyl.auth.vo.RedisKeyVo;
import com.wyl.auth.vo.RedisValueVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Description
 * @Author WuYiLong
 * @Date 2025/4/20 22:12
 */
@Service
public class RedisServiceImpl implements RedisService {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     *
     指标	说明
     used_memory	Redis 分配器分配的内存总量(字节)
     used_memory_human	人类可读格式的 used_memory
     used_memory_rss	操作系统角度看 Redis 占用的内存(常驻集大小)
     used_memory_peak	Redis 内存消耗的峰值
     used_memory_lua	Lua 引擎使用的内存
     * @return
     */
    @Override
    public RedisIndexVo getRedisIndex() {
        RedisConnectionFactory connectionFactory = redisTemplate.getConnectionFactory();
        RedisIndexVo redisIndexVo = new RedisIndexVo();
        // 键值总数
        Long size = connectionFactory.getConnection().dbSize();
        redisIndexVo.setKeyCount(size);
        // 命中次数
        Properties info = connectionFactory.getConnection().info();
        long hits = Long.parseLong(info.getProperty("keyspace_hits"));
        long misses = Long.parseLong(info.getProperty("keyspace_misses"));
        redisIndexVo.setHitFailCount(misses);
        redisIndexVo.setHitSuccessCount(hits);
        // 内存
        String usedMemory = info.getProperty("used_memory");
        String totalSystemMemory = info.getProperty("total_system_memory");
        String usedMemoryPeak = info.getProperty("used_memory_peak");
        String usedMemoryHuman = info.getProperty("used_memory_human");

        BigDecimal bigDecimal1 = new BigDecimal(usedMemory);
        BigDecimal bigDecimal2 = NumberUtil.div(bigDecimal1, 1024).setScale(2, BigDecimal.ROUND_HALF_UP);
        redisIndexVo.setUsedMemory(bigDecimal2.toString()+"k");
        redisIndexVo.setTotalSystemMemory(totalSystemMemory);
        BigDecimal bigDecimal = new BigDecimal(usedMemoryPeak);
        BigDecimal div = NumberUtil.div(bigDecimal, 1024).setScale(2, BigDecimal.ROUND_HALF_UP);
        redisIndexVo.setUsedMemoryPeak(div.toString()+"k");
        redisIndexVo.setUsedMemoryHuman(usedMemoryHuman);
        // 启动时间，版本号,模式
        String redisVersion = info.getProperty("redis_version");
        String redisMode = info.getProperty("redis_mode");
        String uptimeInSeconds = info.getProperty("uptime_in_seconds");
        redisIndexVo.setRedisVersion(redisVersion);
        redisIndexVo.setRedisMode(redisMode);
        redisIndexVo.setStartTime(formatUptime(Long.parseLong(uptimeInSeconds)));
        return redisIndexVo;
    }

    private static String formatUptime(long seconds) {
        long days = seconds / (24 * 3600);
        long hours = (seconds % (24 * 3600)) / 3600;
        long minutes = (seconds % 3600) / 60;
        long remainingSeconds = seconds % 60;
        return String.format("%d天 %d小时 %d分钟 %d秒", days, hours, minutes, remainingSeconds);
    }

    @Override
    public List<RedisKeyVo> listKeys(KeyReq keyReq) {
        List<RedisKeyVo> keyVos = new ArrayList<>();
        Set keys = null;
        if(StrUtil.isNotBlank(keyReq.getKey())){
            keys = redisTemplate.keys("*"+keyReq.getKey()+"*");
        }else{
            keys = redisTemplate.keys("*");
        }

        keys.forEach(key -> {
            RedisKeyVo keyVo = new RedisKeyVo();
            keyVo.setKey(key.toString());
            keyVos.add(keyVo);
        });
        return keyVos;
    }

    @Override
    public RedisValueVo getValue(String key) {
        RedisValueVo redisValueVo = new RedisValueVo();
        redisValueVo.setExpire(redisTemplate.getExpire(key, TimeUnit.SECONDS));
        DataType type = redisTemplate.type(key);
        if(type == DataType.STRING){
            try {
                redisValueVo.setValue(redisTemplate.opsForValue().get(key));
            }catch (Exception e){
                redisValueVo.setValue(stringRedisTemplate.opsForValue().get(key));
            }
        }else if(type == DataType.LIST){
            redisValueVo.setValue(redisTemplate.opsForList().range(key, 0, -1));
        }else if(type == DataType.SET){
            redisValueVo.setValue(redisTemplate.opsForSet().members(key));
        } else if (type == DataType.HASH) {
            redisValueVo.setValue(redisTemplate.opsForHash().entries(key));
        } else if (type == DataType.ZSET) {
            redisValueVo.setValue(redisTemplate.opsForZSet().reverseRange(key, 0, -1));
        }
        return redisValueVo;
    }

    @Override
    public void save(RedisDto redisDto) {
        DataType type = redisTemplate.type(redisDto.getKey());
        if(type == DataType.STRING){
            redisTemplate.opsForValue().set(redisDto.getKey(), redisDto.getValue());
        }else if(type == DataType.LIST){
            redisTemplate.opsForList().leftPush(redisDto.getKey(), redisDto.getValue());
        }else if(type == DataType.SET){
            redisTemplate.opsForSet().add(redisDto.getKey(), redisDto.getValue());
        }else if(type == DataType.HASH){
            Map map = JSONUtil.toBean(JSONUtil.toJsonStr(redisDto.getValue()), Map.class, true);
            redisTemplate.opsForHash().putAll(redisDto.getKey(),map);
        }else if(type == DataType.ZSET){
            redisTemplate.opsForZSet().add(redisDto.getKey(), redisDto.getValue(), 0);
        }
    }

    @Override
    public Boolean updateTTL(RedisDto redisDto) {
        return redisTemplate.expire(redisDto.getKey(), redisDto.getTtl(), TimeUnit.SECONDS);
    }

    @Override
    public void deleteKey(List<String> keys) {
        redisTemplate.delete(keys);
    }
}